package actors {
	import __AS3__.vec.Vector;
	
	import common.ActorMaster;
	import common.Tank;
	
	import flash.display.DisplayObject;
	import flash.geom.Point;
	
	import funge.Befunge;
	import funge.IFunge;
	
	import interfaces.IDestroyable;
	
	public class Actor implements IDestroyable {
		protected var _graphic:DisplayObject;
		
		protected var _energy:int;
		protected var _age:int;
		protected var _ageMax:int;
		protected var _size:Number;
		protected var _sizeMax:Number;
		protected var _friction:Number;
		protected var _accel:Number;
		protected var _speed:Number;
		protected var _rotation:Number;
		protected var _x:Number;
		protected var _y:Number;
		
		//invalidate by setting to null every time actor moves
		protected var _loc:Point;

		protected var _availableCommands:Vector.<String>;
		protected var _program:IFunge;
		
		protected var _actorMaster:ActorMaster;
		protected var _tank:Tank;
		
		private var _destroyed:Boolean;

		public function Actor() {
			_actorMaster = ActorMaster.instance;
			_tank = Tank.instance;
			_age = 0;
//			_alus = new Vector.<IALU>();
			_destroyed = false;
		}
		
		public function init():void {
			
		}

		public function update():Boolean {
			if (_program)
				_program.update();
//			if (_alus.length > 0)
//				_alus[aluIndex].update();

			//age fish
			_age ++
			if (_age > _ageMax) {
				_energy -= _age - _ageMax;
				
				//DEBUG:
//				if (this is Crud)
//					trace("crud is dying of age!");
			}
				
			//grow
			if (_size < _sizeMax) {
				_size += 0.005;
				if (_size > _sizeMax)
					_size = _sizeMax;
				_actorMaster.removeActorGraphic(_graphic);
				generateGraphic();
				_actorMaster.addActorGraphic(_graphic);
				
				_energy --;
			}
			
			_speed *= _friction;
			
			//cap speed
			if (_speed > speedMax)
				_speed = speedMax;
			else if (_speed < -speedMax)
				_speed = -speedMax;
			
			//move actor
			_x += Math.cos(_rotation) * _speed;
			_y += Math.sin(_rotation) * _speed;
			
			//apply current
			_x += Math.cos(_tank.currentDirection) * _tank.currentSpeed;
			_y += Math.sin(_tank.currentDirection) * _tank.currentSpeed;
			
			//screen wrap
			_x %= _tank.width;
			if (_x < 0)
				_x = Math.abs(_x) + _tank.width;
			
			//block from going above or below screen
			if (_y < 0)
				_y = 0;
				
			if (_y > _tank.height)
				_y = _tank.height;

			//invalidate location
			_loc = null;
			
			render();
			
			if (_energy <= 0)
				return false;
				
			return true;
		}
		
//		public function addALU(aluIn:IALU):void {
//			_alus.push(aluIn);
//		}
		
		protected function generateGraphic():void {
			throw new Error("Actor.generateGraphic must be overidden in sub class.");
		}
		
		//commands
		public function turnUp():void {
			//TODO: make bigger guys turn slower
			_rotation -= 0.1;
		}
		
		public function turnDown():void {
			_rotation += 0.1;
		}
		
		public function goForward():void {
			_speed += _accel;
			_energy -= _size;
		}
		
		public function goBackward():void {
			_speed -= _accel;
			_energy -= _size;
		}
		
		//only for fish?
		public function eat():void {
		}
		
		public function mate():void {
		}
		
		public function sleep():void {
			//Do nothing
		}
		
		//internal commands
		private function render():void {
			_graphic.x = _x;
			_graphic.y = _y;
			
			_graphic.rotation = _rotation * 180 / Math.PI - 90;
		}
		
		//simple getters/setters
		public function get energy():int							{return _energy;}
		public function get energyMax():int							{return _size * 10000;}
		public function get size():Number							{return _size;}
		public function get sizeMax():Number						{return _sizeMax;}
		public function get friction():Number						{return _friction;}
		public function get accel():Number							{return _accel;}
		public function get speedMax():Number						{return _speed * 8;}
		public function get x():Number								{return _x;}
		public function get y():Number								{return _y;}
		public function get graphic():DisplayObject					{return _graphic;}
		public function get availableCommands():Vector.<String>		{return _availableCommands;}
		public function get eatRange():Number				 		{return _size * 1.5;}
		public function get mateRange():Number 						{return _size;}
		public function get program():IFunge						{return _program;}
		public function get destroyed():Boolean 					{return _destroyed;}
		
		public function set sizeMax(sizeMaxIn:Number):void			{_sizeMax = sizeMaxIn;}
		public function set accel(accelIn:Number):void				{_accel = accelIn;}
		public function set program(programIn:IFunge):void			{_program = programIn;}
		
		//complex getters/setters
		public function get loc():Point {
			if (!_loc)
				_loc = new Point(_x, _y);
			
			return _loc;
		}
		
		public function set energy(energyIn:int):void {
			_energy = energyIn;
			if (_energy > energyMax)
				_energy = energyMax;
		}
		public function get mature():Boolean {
			if (_size == _sizeMax)
				return true;
				
			return false;
		}
		
		public function get memory():Array {
			return [energy,
				energyMax,
				_age,
				_ageMax,
				size,
				sizeMax,
				friction,
				accel,
				_speed,
				speedMax,
				x,
				y,
				eatRange,
				mateRange,
				mature,
				_rotation];
		}
		
		public function get observableProps():Array {
			return [_age,
				size,
				_speed,
				x,
				y,
				mature,
				_rotation];
		}
		
		public function destroy():void {
			if (destroyed)
				return;
			_destroyed = true;
			
			if (_graphic.parent)	
				_graphic.parent.removeChild(_graphic);
			
			_graphic = null;
			
			_program.destroy();
			
			_actorMaster = null;
			_tank = null;
		}
	}
}
